# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: xguo

import math

from coin.base.config import Config

# Okex
from coin.exchange.okex.order_gateway import OkexOrderGateway
from coin.exchange.okex.kr_rest.product import OkexProduct, OkexCurrency
from coin.exchange.okex.kr_rest.public_client import OkexPublicClient
from coin.exchange.okex.kr_rest.private_client import OkexPrivateClient
from coin.strategy.mm.feed_subsystems import OkexFeedSubsystem

# Binance
from coin.exchange.binance.order_gateway import BinanceOrderGateway
from coin.exchange.binance.kr_rest.product import BinanceProduct, BinanceCurrency
from coin.exchange.binance.kr_rest.public_client import BinancePublicClient
from coin.exchange.binance.kr_rest.private_client import BinancePrivateClient
from coin.strategy.mm.feed_subsystems import BinanceFeedSubsystem

# Huobi
from coin.exchange.huobi.order_gateway import HuobiOrderGateway
from coin.exchange.huobi.kr_rest.product import HuobiProduct, HuobiCurrency
from coin.exchange.huobi.kr_rest.public_client import HuobiPublicClient
from coin.exchange.huobi.kr_rest.private_client import HuobiPrivateClient
from coin.strategy.mm.feed_subsystems import HuobiFeedSubsystem

# Bitfinex
from coin.exchange.bitfinex_v2.order_gateway import BitfinexV2OrderGateway
from coin.exchange.bitfinex_v2.kr_rest.product import BitfinexProduct, BitfinexCurrency
from coin.exchange.bitfinex_v2.kr_rest.public_client import BitfinexPublicClient
from coin.exchange.bitfinex_v2.kr_rest.private_client import BitfinexPrivateClient
from coin.strategy.mm.feed_subsystems import BitfinexV2FeedSubsystem

# Bithumb
from coin.exchange.bithumb.order_gateway import BithumbOrderGateway
from coin.exchange.bithumb.kr_rest.product import BithumbProduct, BithumbCurrency
from coin.exchange.bithumb.kr_rest.public_client import BithumbPublicClient
from coin.exchange.bithumb.kr_rest.private_client import BithumbPrivateClient
from coin.strategy.mm.feed_subsystems import BithumbFeedSubsystem

from coin.exchange.order_gateway.test.hk_spot_og_test_util import (
    gen_og_test_class,
    OGFixture,
)
from coin.proto.coin_query_pb2 import ProductOrderElement
import coin.proto.coin_order_enums_pb2 as coin_order


def okex_fixture():
  currencies = [OkexCurrency.FromStrCurrency(currency) for currency in ('XRP', 'BTC')]
  product = OkexProduct.FromStr('XRP-BTC')
  config = Config.from_config_filename('okex_pilot02')
  og = OkexOrderGateway(currencies, [product], config)
  public_client = OkexPublicClient()
  private_client = OkexPrivateClient(config.key_file)

  def okex_gen_order(last_price, mode):
    qty = 1.0
    if mode == 'passive':
      sell_price = int((last_price * 1.10) * 1e8) / 1e8
      buy_price = int((last_price * 0.90) * 1e8) / 1e8
    elif mode == 'aggressive':
      sell_price = int(last_price * 1e8 * 0.98) / 1e8
      buy_price = int(last_price * 1e8) / 1e8
    else:
      raise ValueError('Invalid mode: %s' % mode)

    buy_order = ProductOrderElement(price=buy_price, qty=qty * 5, side=coin_order.BUY_ORDER)
    sell_order = ProductOrderElement(price=sell_price, qty=qty, side=coin_order.SELL_ORDER)
    return [buy_order] + [sell_order] * 5

  return OGFixture(
      currencies=currencies,
      product=product,
      config=config,
      order_gateway=og,
      market_type='Spot',
      public_client=public_client,
      private_client=private_client,
      gen_order=okex_gen_order,
      feed_subsystem_cls=OkexFeedSubsystem,
  )


def binance_fixture():
  currencies = [BinanceCurrency.FromStrCurrency(currency) for currency in ('XRP', 'BTC')]
  product = BinanceProduct.FromStr('XRP-BTC')
  config = Config.from_config_filename('binance_pilot01')
  og = BinanceOrderGateway(currencies, [product], config)
  public_client = BinancePublicClient()
  private_client = BinancePrivateClient(config.key_file)

  def binance_gen_order(last_price, mode):
    min_value = 0.001
    if mode == 'passive':
      sell_price = int((last_price * 1.10) * 1e8) / 1e8
      buy_price = int((last_price * 0.90) * 1e8) / 1e8
    elif mode == 'aggressive':
      sell_price = int(last_price * 1e8) / 1e8
      buy_price = int(last_price * 1e8) / 1e8
    else:
      raise ValueError('Invalid mode: %s' % mode)

    sell_qty = math.ceil(min_value / sell_price)
    buy_qty = math.ceil(min_value / buy_price)
    buy_order = ProductOrderElement(price=buy_price, qty=buy_qty * 5, side=coin_order.BUY_ORDER)
    sell_order = ProductOrderElement(price=sell_price, qty=sell_qty, side=coin_order.SELL_ORDER)
    return [sell_order] * 5 + [buy_order]

  return OGFixture(
      currencies=currencies,
      product=product,
      config=config,
      order_gateway=og,
      market_type='Spot',
      public_client=public_client,
      private_client=private_client,
      gen_order=binance_gen_order,
      feed_subsystem_cls=BinanceFeedSubsystem,
  )


def huobi_fixture():
  currencies = [HuobiCurrency.FromStrCurrency(currency) for currency in ('XRP', 'USDT')]
  product = HuobiProduct.FromStr('XRP-USDT')
  config = Config.from_config_filename('huobi_pilot01')
  og = HuobiOrderGateway(currencies, [product], config)
  public_client = HuobiPublicClient()
  private_client = HuobiPrivateClient(config.key_file)

  def huobi_gen_order(last_price, mode):
    if mode == 'passive':
      sell_price = int((last_price * 1.10) * 1e4) / 1e4
      buy_price = int((last_price * 0.90) * 1e4) / 1e4
    elif mode == 'aggressive':
      sell_price = int(last_price * 1e4) / 1e4
      buy_price = int(last_price * 1e4) / 1e4
    else:
      raise ValueError('Invalid mode: %s' % mode)

    qty = 1.0
    buy_order = ProductOrderElement(price=buy_price, qty=qty * 5, side=coin_order.BUY_ORDER)
    sell_order = ProductOrderElement(price=sell_price, qty=qty, side=coin_order.SELL_ORDER)
    return [buy_order] + [sell_order] * 5

  return OGFixture(
      currencies=currencies,
      product=product,
      config=config,
      order_gateway=og,
      market_type='Spot',
      public_client=public_client,
      private_client=private_client,
      gen_order=huobi_gen_order,
      feed_subsystem_cls=HuobiFeedSubsystem,
  )


def bitfinex_fixture():
  currencies = [BitfinexCurrency.FromStrCurrency(currency) for currency in ('BTC', 'USD')]
  product = BitfinexProduct.FromStr('BTC-USD')
  config = Config.from_config_filename('bitfinex_pilot01')
  og = BitfinexV2OrderGateway(currencies, [product], config)
  public_client = BitfinexPublicClient()
  private_client = BitfinexPrivateClient(config.key_file)

  def bitfinex_gen_order(last_price, mode):
    qty = 0.002
    if mode == 'passive':
      sell_price = int((last_price * 1.20) * 10) / 10
      buy_price = int((last_price * 0.80) * 10) / 10
    elif mode == 'aggressive':
      sell_price = int(last_price * 10) / 10
      buy_price = int(last_price * 10) / 10
    else:
      raise ValueError('Invalid mode: %s' % mode)

    buy_order = ProductOrderElement(price=buy_price, qty=qty * 5, side=coin_order.BUY_ORDER)
    sell_order = ProductOrderElement(price=sell_price, qty=qty, side=coin_order.SELL_ORDER)
    return [buy_order] + [sell_order] * 5

  return OGFixture(
      currencies=currencies,
      product=product,
      config=config,
      order_gateway=og,
      market_type='Spot',
      public_client=public_client,
      private_client=private_client,
      gen_order=bitfinex_gen_order,
      feed_subsystem_cls=BitfinexV2FeedSubsystem,
  )


def bithumb_fixture():
  currencies = [BithumbCurrency.FromStrCurrency(currency) for currency in ('BTC', 'KRW')]
  product = BithumbProduct.FromStr('BTC-KRW')
  config = Config.from_config_filename('bithumb')
  og = BithumbOrderGateway(config, currencies, [product])
  public_client = BithumbPublicClient()
  private_client = BithumbPrivateClient(config.key_file)

  def bithumb_gen_order(last_price, mode):
    qty = 0.002
    if mode == 'passive':
      sell_price = int(last_price * 1.20 / 1000) * 1000
      buy_price = int(last_price * 0.80 / 1000) * 1000
    elif mode == 'aggressive':
      sell_price = int(last_price / 1000) * 1000
      buy_price = int(last_price / 1000) * 1000
    else:
      raise ValueError('Invalid mode: %s' % mode)

    buy_order = ProductOrderElement(price=buy_price, qty=qty, side=coin_order.BUY_ORDER)
    sell_order = ProductOrderElement(price=sell_price, qty=qty, side=coin_order.SELL_ORDER)
    return buy_order, sell_order

  return OGFixture(
      currencies=currencies,
      product=product,
      config=config,
      order_gateway=og,
      market_type='Spot',
      public_client=public_client,
      private_client=private_client,
      gen_order=bithumb_gen_order,
      feed_subsystem_cls=BithumbFeedSubsystem,
  )


TestOkexOG = gen_og_test_class(okex_fixture)
TestBinanceOG = gen_og_test_class(binance_fixture)
TestHuobiOG = gen_og_test_class(huobi_fixture)
TestBitfinexOG = gen_og_test_class(bitfinex_fixture)
TestBithumbOG = gen_og_test_class(bithumb_fixture)
